﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using NativeWiFiLibrary;
using WiFiChannelSpread.GoogleApi;
using System.Diagnostics;

namespace WiFiChannelSpread
{
    /// <summary>
    /// Interaction logic for NetworkDetailsControl.xaml
    /// </summary>
    public partial class NetworkDetailsControl : UserControl
    {
        private AccessPoint _selectedAccessPoint;

        public event StatusChangeEventHandler StatusChanged;
        public event ExceptionEventHandler ExceptionHandler;

        public Network MyNetwork { get { return _selectedAccessPoint.Network; } }

        public AccessPoint MyAccessPoint { get { return _selectedAccessPoint; } }

        public Brush HighlightColor
        {
            get
            {
                if (MyNetwork.IsConnected)
                {
                    return new SolidColorBrush(Color.FromRgb(0, 176, 0));
                }
                else if (MyNetwork.HasProfile)
                {
                    return new SolidColorBrush(Color.FromRgb(80, 80, 250));
                }
                else
                {
                    return Brushes.Silver;
                }
            }
        }

        public string NetworkName
        {
            get { return string.IsNullOrEmpty(MyNetwork.Ssid) ? "* Anonymous" : MyNetwork.Ssid; }
        }

        public string PhyTypeText
        {
            get
            {
                string standard;
                switch (MyAccessPoint.PhyType)
                {
                    case PhyType.Hrdsss: standard = "802.11b"; break;
                    case PhyType.Erp: standard = "802.11g"; break;
                    case PhyType.Ht: standard = "802.11n"; break;
                    default: standard = MyAccessPoint.PhyType.ToString(); break;
                }

                return standard;
            }
        }

        public NetworkDetailsControl(AccessPoint selectedAccessPoint)
        {
            this._selectedAccessPoint = selectedAccessPoint;

            InitializeComponent();

            ContentSection.Height = 0;
            BackgroundRect.Opacity = 0;
            this.Loaded += new RoutedEventHandler(NetworkDetailsControl_Loaded);

            DisconnectLabel.Visibility = this.MyNetwork.IsConnected ? Visibility.Visible : Visibility.Collapsed;
            ConnectLabel.Visibility = (this.MyNetwork.IsConnected || !this.MyNetwork.IsNetworkConnectable) ? Visibility.Collapsed : Visibility.Visible;
            ProfileActionSection.Visibility = this.MyNetwork.HasProfile ? Visibility.Visible : Visibility.Collapsed;

            if (!MyNetwork.IsConnected && !MyNetwork.IsNetworkConnectable)
            {
                ConnectedLabel.Content = "No (not connectable)";
            }
            else
            {
                ConnectedLabel.Content = MyNetwork.IsConnected ? "Yes" : "No";
            }

            if (!MyNetwork.IsSecurityEnabled)
            {
                SecurityLabel.Content = string.Format("None ({0} {1})", MyNetwork.DefaultAuthenticationAlgorithmString, MyNetwork.DefaultCipherAlgorithmString);
            }
            else
            {
                SecurityLabel.Content = string.Format("{0} {1}", MyNetwork.DefaultAuthenticationAlgorithmString, MyNetwork.DefaultCipherAlgorithmString);
            }

            ProfileLabel.Content = MyNetwork.HasProfile ? "Yes" : "No";
            NetworkTypeLabel.Content = MyNetwork.BssType.ToString();
            
            List<AccessPoint> accessPoints = MyNetwork.GetAccessPoints();
            AccessPointCountLabel.Visibility = accessPoints.Count > 1 ? Visibility.Visible : Visibility.Collapsed;
            AccessPointCountLabel.Content = string.Format("(1/{0})", accessPoints.Count);

            MacAddressLabel.Content = MyAccessPoint.MacAddress;
            WiFiStandardLabel.Content = PhyTypeText;

            // Geolocation might not not have been loaded yet
            if (MyAccessPoint.Geolocation == null)
            {
                try
                {
                    MyAccessPoint.Geolocation = GeolocationApi.GetGeolocationOfWiFiTower(MyAccessPoint.MacAddress, MyAccessPoint.Ssid, true);
                }
                catch (Exception ex)
                {
                    MyAccessPoint.Geolocation = ex;
                }
            }

            Exception geoException = MyAccessPoint.Geolocation as Exception;
            Location location = MyAccessPoint.Geolocation as Location;

            if (location == null && geoException == null)
            {
                GeolocationLabel.Visibility = Visibility.Collapsed;
                AddressLabel.Text = "Not available";
            }
            else if (geoException != null)
            {
                GeolocationLabel.Visibility = Visibility.Collapsed;
                AddressLabel.Text = geoException.Message;
            }
            else
            {
                StringBuilder text = new StringBuilder();
                if (location.address == null)
                {
                    text.Append("Address not available");
                }
                else
                {
                    if (!string.IsNullOrEmpty(location.address.street)) text.Append(string.Format("{0} {1}", location.address.street, location.address.street_number).Trim());
                    if (!string.IsNullOrEmpty(location.address.postal_code)) text.AppendFormat(", {0}", location.address.postal_code);
                    if (!string.IsNullOrEmpty(location.address.city)) text.AppendFormat(", {0}", location.address.city);
                    if (!string.IsNullOrEmpty(location.address.county)) text.AppendFormat(", {0}", location.address.county);
                    if (!string.IsNullOrEmpty(location.address.region)) text.AppendFormat(", {0}", location.address.region);
                    if (!string.IsNullOrEmpty(location.address.country)) text.AppendFormat(", {0}", location.address.country);
                    if (!string.IsNullOrEmpty(location.accuracy)) text.AppendFormat(" ({0}m)", location.accuracy);
                }

                GeolocationLabel.Content = new Underline(new Run(location.latitude + ", " + location.longitude));
                AddressLabel.Text = text.ToString().TrimStart(',').Trim();
            }
        }

        void NetworkDetailsControl_Loaded(object sender, RoutedEventArgs e)
        {
            AnimationHelper.FadeIn(BackgroundRect, 0.6);
            AnimationHelper.StartDoubleAnimation(ContentSection, Border.HeightProperty, ContentSection.MaxHeight);
        }

        private void Label_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            CloseDetailsControl(false);
        }

        private void CloseDetailsControl(bool requireUpdate)
        {
            AnimationHelper.StartDoubleAnimation(BackgroundRect, Rectangle.OpacityProperty, 0);
            AnimationHelper.StartDoubleAnimation(ContentSection, Border.HeightProperty, 0,
                () =>
                {
                    if (this.Parent != null)
                    {
                        (this.Parent as Panel).Children.Remove(this);
                    }
                });

            if (this.StatusChanged != null)
            {
                this.StatusChanged(this, requireUpdate ? StatusChangeAction.UpdateAsap : StatusChangeAction.None);
            }
        }

        private void DisconnectLabel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            try
            {
                e.Handled = true;
                WlanHelper.SelectedNetworkDevice.Disconnect();
                CloseDetailsControl(true);
            }
            catch (Exception ex)
            {
                HandleException("Failed to disconnect from network.", ex);
            }
        }

        private void HandleException(string message, Exception ex)
        {
            CloseDetailsControl(false);

            if (ExceptionHandler != null)
            {
                ExceptionHandler(this, message, ex);
            }
        }

        private void ConnectLabel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            try
            {
                e.Handled = true;
                this.MyNetwork.Connect();
                CloseDetailsControl(true);
            }
            catch (Exception ex)
            {
                HandleException("Failed to connect to network.", ex);
            }
        }

        private void EditProfileLabel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            try
            {
                e.Handled = true;
                this.MyNetwork.EditProfile();
            }
            catch (Exception ex)
            {
                HandleException("Failed to edit network profile.", ex);
            }
        }

        private void DeleteProfileLabel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            try
            {
                e.Handled = true;
                this.MyNetwork.DeleteProfile();
                CloseDetailsControl(true);
            }
            catch (Exception ex)
            {
                HandleException("Failed to delete network profile.", ex);
            }
        }

        private void MyRoot_MouseDown(object sender, MouseButtonEventArgs e)
        {
            CloseDetailsControl(false);
        }

        private void GeolocationLabel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            try
            {
                Location location = MyAccessPoint.Geolocation as Location;
                string url = string.Format("http://maps.google.com/maps?sll={0},{1}", location.latitude, location.longitude);
                Process.Start(new ProcessStartInfo(url));
                e.Handled = true;
            }
            catch (Exception ex)
            {
                HandleException("Failed to open browser to Google Maps.", ex);
            }
        }
    }
}